#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <pthread.h>
#include <vector>
#include <sstream>
#include <fstream>
#include "Socket.hpp"
#include "log.hpp"

const std::string sep = "\r\n";
const uint16_t defaultport = 8888;
const std::string wwwroot = "./wwwroot";
const std::string homepage = "index.html";
extern Log lg;

class HttpServer;
class ThreadData
{
public:
    ThreadData(int fd, HttpServer *s)
        : sockfd(fd), svr(s)
    {
    }

public:
    int sockfd;      // 客户端连接的socket文件描述符
    HttpServer *svr; // 指向HttpServer实例的指针，用于调用服务器方法--常用
};

class HttpRequest
{
public:
    HttpRequest()
    {
    }
    // 反序列化http的请求
    void Deserialize(std::string req)
    {
        while (true)
        {
            // 查找分隔符位置
            std::size_t pos = req.find(sep);
            // 没找到分隔符则退出循环
            if (pos == std::string::npos)
                break;
            // 提取一行
            std::string temp = req.substr(0, pos);
            // 遇到空行(请求头结束标志)则退出
            if (temp.empty())
                break;
            // 保存请求头行
            req_header.push_back(temp);
            // 从请求中移除已处理部分
            req.erase(0, pos + req.size());
        }
        // 剩下的部分作为请求正文
        test = req;
    }

    // 解析请求头信息
    void Parse()
    {
        // 利用stringstream---->数据类型的转化--->字符串和各种数据类型之间的转化，还能用于按特定格式处理字符串
        // 初始化字符串流---以第一行头信息来进行初始化
        std::stringstream ss(req_header[0]);
        // 默认以空格来分割
        ss >> method >> url >> https_version;
        file_path = wwwroot;
        if (url == "/" || url == "/index.html")
        {
            file_path += "/";
            file_path += homepage;
        }
        else
        {
            file_path += url; //./wwwroot/a/b/c.html
        }
        size_t pos = file_path.rfind(".");
        if (pos == std::string::npos)
            suffix = ".html";
        else
            suffix = file_path.substr(pos);
    }

    void DebugPrint()
    {
        for (auto &line : req_header)
        {
            std::cout << "---------------------------------" << std::endl;
            std::cout << line << std::endl;
        }
        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "https_version: " << https_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
        std::cout << "suffix: " << suffix << std::endl;
    }

public:
    // 储存HTTP请求的头部信息
    std::vector<std::string> req_header;
    // 储存HTTP请求的文本信息
    std::string test;

    // 解析后的结果
    std::string method;
    std::string url;
    std::string https_version;
    std::string file_path; //  ./www.root/a/b/c.html
    std::string suffix;    // 后缀
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport)
        : port_(port)
    {
        content_type.insert({"html", "test/html"});
        content_type.insert({".png", "image/png"});
    }
    ~HttpServer()
    {
    }

public:
    bool Start()
    {
        listensock.Socket();
        listensock.Bind(port_);
        listensock.Listen();
        for (;;)
        {
            uint16_t clientport;
            std::string clientip;

            int sockfd = listensock.Accept(&clientip, &clientport);

            if (sockfd < 0)
                continue;
            lg(Info, "get a new accept...,sockfd:%d", sockfd);
            // 创建线程处理该连接
            pthread_t pid;
            ThreadData *td = new ThreadData(sockfd, this);
            pthread_create(&pid, nullptr, Handler, td);
        }
    }
    // 读取文件内容，用于读取本地文件作为响应体
    std::string ReadHttpContent(const std::string &htmlpath)
    {
        // 以二进制模式打开文件，避免文本模式破坏图片二进制文件
        std::ifstream in(htmlpath, std::ios::binary);
        if (!in.is_open())
            return ""; // 打开失败
        // 获取文件大小
        // 定位到文件末尾
        in.seekg(0, std::ios::end);
        // 获取当前位置（即文件大小）
        auto len = in.tellg();
        // 返回到开头
        in.seekg(0, std::ios::beg);

        // 读取文件内容
        std::string content;
        content.resize(len);
        in.read((char *)content.c_str(), content.size());
        in.close();
        // std::cout<<"content: "<<content<<std::endl;
        return content;
    }
    std::string SuffixToDesc(const std::string &suffix)
    {
        auto iter = content_type.find(suffix);
        if (iter == content_type.end())
            return content_type[".html"];
        else
            return content_type[suffix];
    }
    // 处理http请求--解析请求后读取对应的HTML文件内容
    void Handlerhttp(int sockfd)
    {
        // 接收HTTP请求数据
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer), 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;
            // 解析Http请求
            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse(); // 提取关键信息
            req.DebugPrint();
            // 读取HTML文件内容
            std::string text;
            bool ok = true;
            text = ReadHttpContent(req.file_path);
            // 若文件不存在
            if (text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHttpContent(err_html);
            }
            std::string response_line;
            if (ok == true)
            {
                response_line = "Http/1.0 200 OK";
            }
            else
            {
                response_line = "Http/1.0 404 NOT Found";
            }
            std::string response;
            std::string response_header;
            response_header = "Content-Length: ";
            response_header += std::to_string(text.size());
            response_header += "\r\n";
            response_header += "Content-Type: ";
            response_header += SuffixToDesc(req.suffix);
            response_header += "\r\n";

            response = response_line;
            response += response_header;
            response += "\r\n";
            response += text;
            send(sockfd, response.c_str(), response.size(), 0);
        }
    }
    static void *Handler(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->svr->Handlerhttp(td->sockfd);
        close(td->sockfd);
        return nullptr;
    }

private:
    Sock listensock; // 监听sock对象
    uint16_t port_;  // 端口
    // MIME类型映射表，文件后缀到Content-Type的映射
    std::unordered_map<std::string, std::string> content_type;
};

// #pragma once

// #include <iostream>
// #include <string>
// #include <pthread.h>
// #include <fstream>
// #include <vector>
// #include <sstream>
// #include <sys/types.h>
// #include <sys/socket.h>
// #include <unordered_map>

// #include "Socket.hpp"
// #include "log.hpp"

// const std::string wwwroot="./wwwroot"; // web 根目录
// const std::string sep = "\r\n";
// const std::string homepage = "index.html";

// static const int defaultport = 8082;

// class HttpServer;

// class ThreadData
// {
// public:
//     ThreadData(int fd, HttpServer *s) : sockfd(fd), svr(s)
//     {
//     }

// public:
//     int sockfd;
//     HttpServer *svr;
// };

// class HttpRequest
// {
// public:
//     void Deserialize(std::string req)
//     {
//         while(true)
//         {
//             std::size_t pos = req.find(sep);
//             if(pos == std::string::npos) break;
//             std::string temp = req.substr(0, pos);
//             if(temp.empty()) break;
//             req_header.push_back(temp);
//             req.erase(0, pos+sep.size());
//         }
//         text = req;
//     }
//     // .png:image/png
//     void Parse()
//     {
//         std::stringstream ss(req_header[0]);
//         ss >> method >> url >> http_version;
//         file_path = wwwroot; // ./wwwroot
//         if(url == "/" || url == "/index.html") {
//             file_path += "/";
//             file_path += homepage; // ./wwwroot/index.html
//         }
//         else file_path += url; // /a/b/c/d.html->./wwwroot/a/b/c/d.html

//         auto pos = file_path.rfind(".");
//         if(pos == std::string::npos) suffix = ".html";
//         else suffix = file_path.substr(pos);
//     }
//     // void DebugPrint()
//     // {
//     //     for(auto &line : req_header)
//     //     {
//     //         std::cout << "--------------------------------" << std::endl;
//     //         std::cout << line << "\n\n";
//     //     }

//     //     std::cout << "method: " << method << std::endl;
//     //     std::cout << "url: " << url << std::endl;
//     //     std::cout << "http_version: " << http_version << std::endl;
//     //     std::cout << "file_path: " << file_path << std::endl;
//     //     std::cout << text << std::endl;
//     // }
// public:
//     std::vector<std::string> req_header;
//     std::string text;

//     // 解析之后的结果
//     std::string method;
//     std::string url;
//     std::string http_version;
//     std::string file_path; // ./wwwroot/a/b/c.html 2.png

//     std::string suffix;
// };

// class HttpServer
// {
// public:
//     HttpServer(uint16_t port = defaultport) : port_(port)
//     {
//         content_type.insert({".html", "text/html"});
//         content_type.insert({".png", "image/png"});
//     }
//     bool Start()
//     {
//         listensock_.Socket();
//         listensock_.Bind(port_);
//         listensock_.Listen();
//         for (;;)
//         {
//             std::string clientip;
//             uint16_t clientport;
//             int sockfd = listensock_.Accept(&clientip, &clientport);
//             if (sockfd < 0)
//                 continue;
//             lg(Info, "get a new connect, sockfd: %d", sockfd);
//             pthread_t tid;
//             ThreadData *td = new ThreadData(sockfd, this);
//             pthread_create(&tid, nullptr, ThreadRun, td);
//         }
//     }
//     static std::string ReadHtmlContent(const std::string &htmlpath)
//     {
//         // 坑
//         std::ifstream in(htmlpath, std::ios::binary);
//         if(!in.is_open()) return "";

//         in.seekg(0, std::ios_base::end);
//         auto len = in.tellg();
//         in.seekg(0, std::ios_base::beg);

//         std::string content;
//         content.resize(len);

//         in.read((char*)content.c_str(), content.size());
//         //std::string content;
//         //std::string line;
//         //while(std::getline(in, line))
//         //{
//         //    content += line;
//         //}

//         in.close();

//         return content;
//     }
//     std::string SuffixToDesc(const std::string &suffix)
//     {
//         auto iter = content_type.find(suffix);
//         if(iter == content_type.end()) return content_type[".html"];
//         else return content_type[suffix];
//     }
//     void HandlerHttp(int sockfd)
//     {
//         char buffer[10240];
//         ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // bug
//         if (n > 0)
//         {
//             buffer[n] = 0;
//             std::cout << buffer << std::endl; // 假设我们读取到的就是一个完整的，独立的http 请求
//             HttpRequest req;
//             req.Deserialize(buffer);
//             req.Parse();
//             //req.DebugPrint();

//             //std::string path = wwwroot;
//             //path += url; // wwwroot/a/a/b/index.html

//             // 返回响应的过程
//             std::string text;
//             bool ok = true;
//             text = ReadHtmlContent(req.file_path); // 失败？
//             if(text.empty())
//             {
//                 ok = false;
//                 std::string err_html = wwwroot;
//                 err_html += "/";
//                 err_html += "err.html";
//                 text = ReadHtmlContent(err_html);
//             }

//             std::string response_line;
//             if(ok)
//                 response_line = "HTTP/1.0 200 OK\r\n";
//             else
//                 response_line = "HTTP/1.0 404 Not Found\r\n";

//             response_line = "HTTP/1.0 302 Found\r\n";
//             std::string response_header = "Content-Length: ";
//             response_header += std::to_string(text.size()); // Content-Length: 11
//             response_header += "\r\n";
//             response_header += "Content-Type: ";
//             response_header += SuffixToDesc(req.suffix);
//             response_header += "\r\n";
//             response_header += "Set-Cookie: name=haha&&passwd=12345";
//             response_header += "\r\n";

//             // response_header += "Location: https://www.qq.com\r\n";
//             std::string blank_line = "\r\n"; // \n

//             std::string response = response_line;
//             response += response_header;
//             response += blank_line;
//             response += text;

//             send(sockfd, response.c_str(), response.size(), 0);
//         }
//         close(sockfd);
//     }
//     static void *ThreadRun(void *args)
//     {
//         pthread_detach(pthread_self());
//         ThreadData *td = static_cast<ThreadData *>(args);
//         td->svr->HandlerHttp(td->sockfd);
//         delete td;
//         return nullptr;
//     }
//     ~HttpServer()
//     {
//     }

// private:
//     Sock listensock_;
//     uint16_t port_;
//     std::unordered_map<std::string, std::string> content_type;
// };